{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 配置\n",
    "安装jupyter notebook、pytoch、matplotlib等包\n",
    "## 基本数据操作\n",
    "Tensor， 基本和numpy数组差不多，和matlab里一些操作也很像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.0653e-38, 1.0194e-38],\n",
      "        [4.6838e-39, 8.4489e-39],\n",
      "        [9.6429e-39, 8.4490e-39],\n",
      "        [9.6429e-39, 9.2755e-39],\n",
      "        [1.0286e-38, 9.0919e-39]])\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "#创建tensor\n",
    "x = torch.empty((5,2))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.2698, 0.5864, 0.8902],\n",
      "        [0.2143, 0.9121, 0.4713],\n",
      "        [0.3524, 0.8525, 0.6389],\n",
      "        [0.5843, 0.0419, 0.1382],\n",
      "        [0.3696, 0.7504, 0.1698]])\n"
     ]
    }
   ],
   "source": [
    "#随机建立一个tensor 范围0-1\n",
    "x = torch.rand((5,3))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 2.5208,  0.7523, -1.2726, -0.3358],\n",
       "        [ 0.2356, -1.7474,  0.6377, -1.3223],\n",
       "        [-0.7516, -1.4248, -0.8945, -0.1546],\n",
       "        [-1.1864,  0.5417,  0.1103, -1.5674],\n",
       "        [-1.4791, -1.3699,  1.1319, -0.0894]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#符合标准正态分布的随机建立\n",
    "torch.randn((5,4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10.])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.linspace(1,10,steps=10)\n",
    "#线性地创建tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 0.5736, -0.2414, -1.3859,  6.2779], dtype=torch.float64)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.normal(torch.arange(1,5, dtype = torch.float64), torch.arange(1,5, dtype = torch.float64))\n",
    "#离散正态分布 这个用途存疑，没遇到过"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x : tensor([3., 1., 3., 4.]) type : <class 'torch.Tensor'>\n",
      "y : tensor([[0.5956, 0.2546],\n",
      "        [0.6275, 0.5881],\n",
      "        [0.5457, 0.3365],\n",
      "        [0.5120, 0.6354],\n",
      "        [0.6306, 0.7218]], dtype=torch.float64) type : <class 'torch.Tensor'>\n",
      "z : [[0.59555421 0.25459096]\n",
      " [0.62746029 0.58812724]\n",
      " [0.54573941 0.33650778]\n",
      " [0.51202086 0.63536874]\n",
      " [0.630643   0.72176699]] type : <class 'numpy.ndarray'>\n",
      " \n"
     ]
    }
   ],
   "source": [
    "#从列表中建立\n",
    "import numpy as np\n",
    "data = np.random.rand(5,2)\n",
    "x = torch.tensor([3.0,1,3,4])\n",
    "y = torch.from_numpy(data)\n",
    "z = y.numpy()\n",
    "print('x : {} type : {}\\ny : {} type : {}\\nz : {} type : {}\\n '.format(x,type(x),y,type(y),z,type(z)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.]], dtype=torch.float64)\n",
      "tensor([[-2.8108,  1.6489, -1.6023],\n",
      "        [ 0.4254,  0.4217,  1.2425],\n",
      "        [-1.6093,  0.2520, -0.2242],\n",
      "        [-0.0700, -0.1271, -1.2428],\n",
      "        [ 1.4094, -1.2336, -1.1381]])\n"
     ]
    }
   ],
   "source": [
    "#其他地建立方式\n",
    "x = x.new_ones(5, 3, dtype=torch.float64)  # 返回的tensor默认具有相同的torch.dtype和torch.device\n",
    "print(x)\n",
    "\n",
    "x = torch.randn_like(x, dtype=torch.float) # 指定新的数据类型\n",
    "print(x) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([5, 3])\n",
      "torch.Size([5, 3])\n"
     ]
    }
   ],
   "source": [
    "#获取tensor的形状\n",
    "print(x.size())\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Torch Tensor的基础操作\n",
    "\n",
    "|               函数                |           功能            |\n",
    "| :-------------------------------: | :-----------------------: |\n",
    "|          Tensor(*sizes)           |       基础构造函数        |\n",
    "|           tensor(data,)           |  类似np.array的构造函数   |\n",
    "|           ones(*sizes)            |         全1Tensor         |\n",
    "|           zeros(*sizes)           |         全0Tensor         |\n",
    "|            eye(*sizes)            |    对角线为1，其他为0     |\n",
    "|          arange(s,e,step          |    从s到e，步长为step     |\n",
    "|        linspace(s,e,steps)        | 从s到e，均匀切分成steps份 |\n",
    "|        rand/randn(*sizes)         |       均匀/标准分布       |\n",
    "| normal(mean,std)/uniform(from,to) |     正态分布/均匀分布     |\n",
    "|            randperm(m)            |         随机排列          |\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这里出现了一个dtype（data type 数据类型嘛）参数，那么pytorch里数据类型有哪些呢？\n",
    "![imgs/datatype.jpg](attachment:imgs/datatype.jpg)\n",
    "\n",
    "## 算术操作\n",
    "加减乘，比较常见"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-2.1231,  0.6314, -2.0765],\n",
       "        [ 1.0351,  1.0944,  1.0693],\n",
       "        [-1.8506, -0.2468,  1.2153],\n",
       "        [-0.7966, -0.9307, -0.2907],\n",
       "        [ 3.0734,  0.3131, -1.6397]])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = torch.randn(5,3)\n",
    "x+y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[-2.1231,  0.6314, -2.0765],\n",
       "        [ 1.0351,  1.0944,  1.0693],\n",
       "        [-1.8506, -0.2468,  1.2153],\n",
       "        [-0.7966, -0.9307, -0.2907],\n",
       "        [ 3.0734,  0.3131, -1.6397]])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.add(x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[-2.1231,  0.6314, -2.0765],\n",
      "        [ 1.0351,  1.0944,  1.0693],\n",
      "        [-1.8506, -0.2468,  1.2153],\n",
      "        [-0.7966, -0.9307, -0.2907],\n",
      "        [ 3.0734,  0.3131, -1.6397]])\n"
     ]
    }
   ],
   "source": [
    "# inplace 操作 ，就地操作，直接修改y，而不是返回一个新的值\n",
    "# 不需要 y = y.add(x)\n",
    "y.add_(x)\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 索引\n",
    "我们还可以使用类似NumPy的索引操作来访问`Tensor`的一部分，需要注意的是：**索引出来的结果与原数据共享内存，也即修改一个，另一个会跟着修改。** \n",
    "索引操作在python  里，是直接对对象进行操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[-1.8108,  2.6489, -0.6023],\n",
      "        [ 0.4254,  0.4217,  1.2425],\n",
      "        [-1.6093,  0.2520, -0.2242],\n",
      "        [-0.0700, -0.1271, -1.2428],\n",
      "        [ 1.4094, -1.2336, -1.1381]])\n",
      "before : -1.8108251094818115\n",
      "after 100\n",
      "tensor([[-1.8108,  2.6489, -0.6023],\n",
      "        [ 0.4254,  0.4217,  1.2425],\n",
      "        [-1.6093,  0.2520, -0.2242],\n",
      "        [-0.0700, -0.1271, -1.2428],\n",
      "        [ 1.4094, -1.2336, -1.1381]])\n"
     ]
    }
   ],
   "source": [
    "y = x[0,:]\n",
    "y += 1\n",
    "print(x)\n",
    "\n",
    "# 严格说是切片索引是直接操作原始数据，而普通的索引就是简单的 赋值。\n",
    "y = x[0,0]\n",
    "print('before : {}'.format(y))\n",
    "y = 100\n",
    "print('after {}'.format(y))\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|              函数               |                         功能                          |\n",
    "| :-----------------------------: | :---------------------------------------------------: |\n",
    "| index_select(input, dim, index) |      在指定维度dim上选取，比如选取某些行、某些列      |\n",
    "|   masked_select(input, mask)    |       例子如上，a[a>0]，使用ByteTensor进行选取        |\n",
    "|         non_zero(input)         |                     非0元素的下标                     |\n",
    "|    gather(input, dim, index)    | 根据index，在dim维度上选取数据，输出的size与index一样 |\n",
    "\n",
    "除了常用的索引选择数据之外，PyTorch还提供了一些高级的选择函数:\n",
    "\n",
    "mask操作可以这样做"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[2.8175e+20, 0.0000e+00, 0.0000e+00],\n",
      "        [0.0000e+00, 0.0000e+00, 0.0000e+00],\n",
      "        [2.1019e-44, 0.0000e+00, 4.2538e-11]])\n",
      "tensor([[ 0.8381, -0.2748,  0.6950],\n",
      "        [-0.5977,  1.0487, -0.4596],\n",
      "        [-1.7324,  1.2727, -1.0633]])\n",
      "tensor([[0, 1, 0],\n",
      "        [1, 0, 1],\n",
      "        [1, 0, 1]], dtype=torch.uint8)\n",
      "tensor([-0.2748, -0.5977, -0.4596, -1.7324, -1.0633])\n",
      "tensor([-0.2748, -0.5977, -0.4596, -1.7324, -1.0633])\n",
      "tensor([[0., 1., 0.],\n",
      "        [1., 0., 1.],\n",
      "        [1., 0., 1.]], dtype=torch.float64)\n",
      "tensor([[0., 1., 0.],\n",
      "        [1., 0., 1.],\n",
      "        [1., 0., 1.]])\n",
      "tensor([[0., 1., 0.],\n",
      "        [1., 0., 1.],\n",
      "        [1., 0., 1.]])\n",
      "tensor([[ 0.0000, -0.2748,  0.0000],\n",
      "        [-0.5977,  0.0000, -0.4596],\n",
      "        [-1.7324,  0.0000, -1.0633]])\n",
      "上面三种方法都一样\n",
      "[[0.28061843 0.03580725 0.40071972]\n",
      " [0.72136432 0.41048705 0.52899718]\n",
      " [0.96488411 0.08075699 0.53774382]\n",
      " [0.93238773 0.59511078 0.11153985]\n",
      " [0.18631414 0.64031574 0.30824729]]\n",
      "[[ True False  True]\n",
      " [ True False  True]\n",
      " [ True  True False]\n",
      " [ True False  True]\n",
      " [ True  True  True]]\n",
      "[0.28061843 0.40071972 0.72136432 0.52899718 0.96488411 0.08075699\n",
      " 0.93238773 0.11153985 0.18631414 0.64031574 0.30824729]\n",
      "[[0.28061843 0.         0.40071972]\n",
      " [0.72136432 0.         0.52899718]\n",
      " [0.96488411 0.08075699 0.        ]\n",
      " [0.93238773 0.         0.11153985]\n",
      " [0.18631414 0.64031574 0.30824729]]\n"
     ]
    },
    {
     "ename": "RuntimeError",
     "evalue": "expected backend CPU and dtype Float but got backend CPU and dtype Byte",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-13-507e26f338d8>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     16\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     17\u001b[0m     \u001b[1;31m#直接做会出错，先类型转换一下\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m \u001b[1;33m*\u001b[0m \u001b[0mmask\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     19\u001b[0m \u001b[1;32mfinally\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     20\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmask\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfloat64\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mRuntimeError\u001b[0m: expected backend CPU and dtype Float but got backend CPU and dtype Byte"
     ]
    }
   ],
   "source": [
    "#按照size构造tensor 大写\n",
    "x = torch.Tensor(3,3)\n",
    "print(x)\n",
    "\n",
    "#随机初始一个\n",
    "x = torch.randn(3,3)\n",
    "print(x)\n",
    "print(x < 0.5)\n",
    "\n",
    "#取出符合要求的元素\n",
    "print(x[x<0.5])\n",
    "print(torch.masked_select(x, x < 0.5))\n",
    "\n",
    "#这里需要对类型进行转换\n",
    "mask = x<0.5\n",
    "try:\n",
    "    #直接做会出错，先类型转换一下\n",
    "    print(x * mask)\n",
    "finally:\n",
    "    print(mask.type(torch.float64))\n",
    "    print(mask.float())\n",
    "    print(mask.type_as(x))\n",
    "    print(x*mask.float())\n",
    "    \n",
    "    print('上面三种方法都一样')\n",
    "\n",
    "    w = np.random.rand(5,3)\n",
    "    print(w)\n",
    "    list_mask=np.random.rand(*w.shape) < 0.5\n",
    "    print((list_mask))\n",
    "    #注意两种mask 方式的不一样\n",
    "    print(w[list_mask])\n",
    "    print(w * list_mask)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 改变形状\n",
    "用`view()`来改变`Tensor`的形状："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0.5882,  1.0098,  0.6289],\n",
      "        [ 0.4073, -1.7818, -1.7547],\n",
      "        [ 0.3263,  0.0589, -0.1307]])\n",
      "tensor([[ 0.5882,  1.0098,  0.6289,  0.4073, -1.7818, -1.7547,  0.3263,  0.0589,\n",
      "         -0.1307]])\n",
      "tensor([[ 6.6600e+02,  1.0098e+00,  6.2886e-01,  4.0729e-01, -1.7818e+00,\n",
      "         -1.7547e+00,  3.2627e-01,  5.8898e-02, -1.3067e-01]])\n",
      "tensor([[ 6.6600e+02,  1.0098e+00,  6.2886e-01],\n",
      "        [ 4.0729e-01, -1.7818e+00, -1.7547e+00],\n",
      "        [ 3.2627e-01,  5.8898e-02, -1.3067e-01]])\n",
      "tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=torch.uint8)\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(3,3)\n",
    "print(x)\n",
    "y = x.view(-1,9)\n",
    "print(y)\n",
    "y[0][0] = 666\n",
    "print(y)\n",
    "print(x)\n",
    "print(x.view(y.shape) == y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意`view()`返回的新tensor与源tensor共享内存（其实是同一个tensor），也即更改其中的一个，另外一个也会跟着改变。(顾名思义，view仅仅是改变了对这个张量的观察角度)**\n",
    "\n",
    "使用`clone`还有一个好处是会被记录在计算图中，即梯度回传到副本时也会传到源`Tensor`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 6.6600e+02,  1.0098e+00,  6.2886e-01,  4.0729e-01, -1.7818e+00,\n",
      "         -1.7547e+00,  3.2627e-01,  5.8898e-02, -1.3067e-01]])\n",
      "tensor([[ 8.8800e+02,  1.0098e+00,  6.2886e-01,  4.0729e-01, -1.7818e+00,\n",
      "         -1.7547e+00,  3.2627e-01,  5.8898e-02, -1.3067e-01]])\n",
      "tensor([[ 8.8800e+02,  1.0098e+00,  6.2886e-01],\n",
      "        [ 4.0729e-01, -1.7818e+00, -1.7547e+00],\n",
      "        [ 3.2627e-01,  5.8898e-02, -1.3067e-01]])\n",
      "tensor([[-1.1045, -0.1357, -0.4414],\n",
      "        [-0.5972, -0.6772,  0.7539],\n",
      "        [ 0.4258,  0.7539, -0.1603]])\n",
      "tensor([[ 7.7700e+02, -1.3568e-01, -4.4140e-01, -5.9722e-01, -6.7715e-01,\n",
      "          7.5394e-01,  4.2581e-01,  7.5388e-01, -1.6028e-01]])\n",
      "tensor([[-1.1045, -0.1357, -0.4414],\n",
      "        [-0.5972, -0.6772,  0.7539],\n",
      "        [ 0.4258,  0.7539, -0.1603]])\n"
     ]
    }
   ],
   "source": [
    "y2 = x.reshape(-1,9)\n",
    "#但是此函数并不能保证返回的是其拷贝\n",
    "#不推荐使用reshape函数\n",
    "print(y2)\n",
    "y2[0][0] = 888\n",
    "print(y2)\n",
    "print(x)\n",
    "\n",
    "x = torch.randn(3,3)\n",
    "print(x)\n",
    "#clone() 返回副本 然后再view\n",
    "y3 = x.clone().view(-1,9)\n",
    "y3[0][0] = 777\n",
    "print(y3)\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "另外一个常用的函数就是`item()`, 它可以将一个标量`Tensor`转换成一个Python number："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1.4037])\n",
      "1.4037163257598877\n"
     ]
    }
   ],
   "source": [
    "x = torch.randn(1)\n",
    "print(x)\n",
    "print(x.item())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 线性代数运算\n",
    "PyTorch还支持一些线性函数，这里提一下，免得用起来的时候自己造轮子，具体用法参考官方文档。\n",
    "PyTorch中的`Tensor`支持超过一百种操作，包括转置、索引、切片、数学运算、线性代数、随机数等等，可参考[官方文档](https://pytorch.org/docs/stable/tensors.html)。\n",
    "\n",
    "|               函数               |               功能                |\n",
    "| :------------------------------: | :-------------------------------: |\n",
    "|              trace               |     对角线元素之和(矩阵的迹)      |\n",
    "|               diag               |            对角线元素             |\n",
    "|            triu/tril             | 矩阵的上三角/下三角，可指定偏移量 |\n",
    "|              mm/bmm              |     矩阵乘法，batch的矩阵乘法     |\n",
    "| addmm/addbmm/addmv/addr/badbmm.. |             矩阵运算              |\n",
    "|                t                 |               转置                |\n",
    "|            dot/cross             |             内积/外积             |\n",
    "|             inverse              |             求逆矩阵              |\n",
    "|               svd                |            奇异值分解             |\n",
    "\n",
    "##  广播机制 Broadcast\n",
    "前面我们看到如何对两个形状相同的`Tensor`做按元素运算。当对两个形状不同的`Tensor`按元素运算时，可能会触发广播（broadcasting）机制：先适当复制元素使这两个`Tensor`形状相同后再按元素运算。例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 2.8259,  0.2654, -0.3658],\n",
      "        [-0.4613, -1.0131,  0.2879],\n",
      "        [-1.2214, -2.1509,  0.6675],\n",
      "        [ 0.8397, -0.6470,  0.2313],\n",
      "        [ 0.6809, -0.7530, -0.5659]])\n",
      "tensor([[1],\n",
      "        [2]]) tensor([[1, 2, 3]])\n",
      "tensor([[2, 3, 4],\n",
      "        [3, 4, 5]])\n"
     ]
    },
    {
     "ename": "RuntimeError",
     "evalue": "The size of tensor a (3) must match the size of tensor b (4) at non-singleton dimension 1",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-17-3cfbc7eba8bb>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     16\u001b[0m \u001b[0mx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrandn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m5\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     17\u001b[0m \u001b[0my\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrandn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m4\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m \u001b[0mx\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0my\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     19\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mRuntimeError\u001b[0m: The size of tensor a (3) must match the size of tensor b (4) at non-singleton dimension 1"
     ]
    }
   ],
   "source": [
    "x = torch.randn(5,3)\n",
    "y = torch.randn(3)\n",
    "\n",
    "#注意， 要维度相同呢\n",
    "print(x.add(y))\n",
    "\n",
    "#或者双方都有一个维度为1：\n",
    "x = torch.arange(1,3).view(2,1)\n",
    "y = torch.arange(1,4).view(1,3)\n",
    "print(x,y)\n",
    "print(y.add(x))\n",
    "\n",
    "#inplace 操作不会触发 broadcast操作\n",
    "\n",
    "#不同会怎么样\n",
    "x = torch.randn(5,3)\n",
    "y = torch.randn(4)\n",
    "x.add(y)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2.4 运算的内存开销\n",
    "\n",
    "前面说了，索引、`view`是不会开辟新内存的，而像`y = x + y`这样的运算是会新开内存的，然后将`y`指向新内存。为了演示这一点，我们可以使用Python自带的`id`函数：如果两个实例的ID一致，那么它们所对应的内存地址相同；反之则不同。\n",
    "\n",
    "```python\n",
    "x = torch.tensor([1, 2])\n",
    "y = torch.tensor([3, 4])\n",
    "id_before = id(y)\n",
    "y = y + x\n",
    "print(id(y) == id_before) # False \n",
    "```\n",
    "\n",
    "如果想指定结果到原来的`y`的内存，我们可以使用前面介绍的索引来进行替换操作。在下面的例子中，我们把`x + y`的结果通过`[:]`写进`y`对应的内存中。\n",
    "\n",
    "```python\n",
    "x = torch.tensor([1, 2])\n",
    "y = torch.tensor([3, 4])\n",
    "id_before = id(y)\n",
    "y[:] = y + x\n",
    "print(id(y) == id_before) # True\n",
    "```\n",
    "\n",
    "我们还可以使用运算符全名函数中的`out`参数或者自加运算符`+=`(也即`add_()`)达到上述效果，例如`torch.add(x, y, out=y)`和`y += x`(`y.add_(x)`\n",
    "\n",
    "x = torch.tensor([1, 2])\n",
    "y = torch.tensor([3, 4])\n",
    "id_before = id(y)\n",
    "torch.add(x, y, out=y) # y += x, y.add_(x)\n",
    "print(id(y) == id_before) # True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2.5 `Tensor`和NumPy相互转换\n",
    "\n",
    "我们很容易用`numpy()`和`from_numpy()`将`Tensor`和NumPy中的数组相互转换。但是需要注意的一点是：\n",
    "**这两个函数所产生的的`Tensor`和NumPy中的数组共享相同的内存（所以他们之间的转换很快），改变其中一个时另一个也会改变！！！**\n",
    "\n",
    "> 还有一个常用的将NumPy中的array转换成`Tensor`的方法就是`torch.tensor()`, 需要注意的是，此方法总是会进行数据拷贝（就会消耗更多的时间和空间），所以返回的`Tensor`和原来的数据不再共享内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 1.68143302  0.86940361 -1.74078467]\n",
      " [ 1.34702787  1.04156948  1.06457433]\n",
      " [-0.095788    0.88031317 -0.31062252]]\n",
      "tensor([[ 1.0000,  1.0000,  1.0000],\n",
      "        [ 1.3470,  1.0416,  1.0646],\n",
      "        [-0.0958,  0.8803, -0.3106]], dtype=torch.float64) [[ 1.          1.          1.        ]\n",
      " [ 1.34702787  1.04156948  1.06457433]\n",
      " [-0.095788    0.88031317 -0.31062252]]\n",
      "tensor([[-0.4545, -0.1526, -0.8237],\n",
      "        [ 1.6783,  0.9120,  0.1160],\n",
      "        [ 0.1862, -0.8473,  1.5782]])\n",
      "[[ 2.2200000e+02  2.2200000e+02  2.2200000e+02]\n",
      " [ 1.6783195e+00  9.1200966e-01  1.1595139e-01]\n",
      " [ 1.8618354e-01 -8.4726876e-01  1.5782217e+00]] tensor([[ 2.2200e+02,  2.2200e+02,  2.2200e+02],\n",
      "        [ 1.6783e+00,  9.1201e-01,  1.1595e-01],\n",
      "        [ 1.8618e-01, -8.4727e-01,  1.5782e+00]])\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "x = np.random.randn(3,3)\n",
    "print(x)\n",
    "x_tensor = torch.from_numpy(x)\n",
    "x_tensor[0] = 1\n",
    "print(x_tensor, x)\n",
    "\n",
    "x = torch.randn(3,3)\n",
    "print(x)\n",
    "x_np = x.numpy()\n",
    "x_np[0] = 222\n",
    "print(x_np, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 特别提醒[注意Tensor大小写]\n",
    "\n",
    "最重要的区别t.Tensor和t.tensor：不论输入的类型是什么，`t.tensor()`都会进行数据拷贝，不会共享内存；`t.Tensor()`与`Numpy`共享内存，但当Numpy的数据类型和Tensor的类型不一样的时候，数据会被复制，不会共享内存。\n",
    "可使用`t.from_numpy()` 或者 `t.detach()` 将Numpy转为Tensor，与原Numpy数据共享内存。\n",
    "附上实验证明\n",
    "常规转换：使用`t.from_numpy()` 将Numpy转为Tensor，使用 `torch.numpy()` 将Tensor转为Numpy\n",
    "这里两种方式都是共享内存"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所有在CPU上的`Tensor`（除了`CharTensor`）都支持与NumPy数组相互转换。\n",
    "\n",
    "此外上面提到还有一个常用的方法就是直接用`torch.tensor()`将NumPy数组转换成`Tensor`，需要注意的是该方法总是会进行数据拷贝，返回的`Tensor`和原来的数据不再共享内存。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0.84586067 -0.44833491 -0.79411832]\n",
      " [ 2.49791213  0.31611795 -0.94234916]\n",
      " [ 0.56650969 -1.55048646  1.05598356]]\n",
      "tensor([[222.0000, 222.0000, 222.0000],\n",
      "        [  2.4979,   0.3161,  -0.9423],\n",
      "        [  0.5665,  -1.5505,   1.0560]], dtype=torch.float64) [[ 0.84586067 -0.44833491 -0.79411832]\n",
      " [ 2.49791213  0.31611795 -0.94234916]\n",
      " [ 0.56650969 -1.55048646  1.05598356]]\n"
     ]
    }
   ],
   "source": [
    "x = np.random.randn(3,3)\n",
    "print(x)\n",
    "x_np = torch.tensor(x)\n",
    "x_np[0] = 222\n",
    "print(x_np, x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.2.6 `Tensor` on GPU\n",
    "\n",
    "用方法`to()`可以将`Tensor`在CPU和GPU（需要硬件支持）之间相互移动。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0.5542,  1.7384,  1.6803,  0.3049, -0.0626],\n",
      "        [ 0.2323,  1.6934,  0.3984,  2.1927, -0.0714],\n",
      "        [-0.5662,  0.5605,  1.8954,  1.1936,  0.2982],\n",
      "        [ 2.0363,  1.5869, -0.8402,  2.6733, -0.0384],\n",
      "        [ 0.1312,  0.8246,  2.6192,  1.7819,  0.4742]], device='cuda:0')\n",
      "tensor([[ 0.5542,  1.7384,  1.6803,  0.3049, -0.0626],\n",
      "        [ 0.2323,  1.6934,  0.3984,  2.1927, -0.0714],\n",
      "        [-0.5662,  0.5605,  1.8954,  1.1936,  0.2982],\n",
      "        [ 2.0363,  1.5869, -0.8402,  2.6733, -0.0384],\n",
      "        [ 0.1312,  0.8246,  2.6192,  1.7819,  0.4742]], dtype=torch.float64)\n"
     ]
    }
   ],
   "source": [
    "# 以下代码只有在PyTorch GPU版本上才会执行\n",
    "x = torch.randn(5,5)\n",
    "if torch.cuda.is_available():\n",
    "    device = torch.device(\"cuda\")          # GPU\n",
    "    y = torch.ones_like(x, device = device)  # 直接创建一个在GPU上的Tensor\n",
    "\n",
    "    x = x.to(device)                       # 等价于 .to(\"cuda\")\n",
    "    z = x + y\n",
    "    print(z)\n",
    "    print(z.to(\"cpu\", torch.double))       # to()还可以同时更改数据类型"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
